﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpDepend.Storage.DataFormats
{
    /* Exemple
     * [root]
     * {
     *  [Name]
     *  {
     *   [SubName]
     *   {
     *    Value1 = 0;
     *    Value2 = "Hej";
     *   }
     *  }
     *  [Name2]
     *  {
     *   [SubName2]
     *   {
     *    Value1 = 0;
     *    Value2 = "Hej";
     *   }
     *  }
     * }
     */

    /// <summary>
    /// Denna metod behöver använda StringBuilder sen eftersom att operatorn + mellan strängar blir ungefär 150 GÅNGER långsammare!
    /// </summary>
    public class SimpleC : IDataFormat
    {
        const string TAB = "\t";
        StringBuilder data = new StringBuilder();

        public override void Save(CustomNode headNodeToSaveFrom)
        {
            data.Clear();

            WriteScopeStart(headNodeToSaveFrom, 0);
            WriteScopeContent(headNodeToSaveFrom, 0);
            WriteScopeEnd(0);

            Write( StrToByteArray(data.ToString()) );
        }

        public override void Load(CustomNode headNodeToLoadTo)
        {
            data.Clear();

            byte[] data2 = new byte[Length()];
            Read(data2, 0, data2.Length);
            string strData = ByteArrayToStr(data2);

            if (strData.Length > 0)
            {
                ParseScope(strData, headNodeToLoadTo);
            }
        }

        /////////////////////////////////////////////////////////
        // WRITE
        /////////////////////////////////////////////////////////
        void WriteScopeStart(CustomNode node, int subIndex)
        {
            string tabs = getTabs(subIndex);

            data.Append(tabs);
            data.Append("[");
            data.Append(node.Name);
            data.Append("]\r\n");
            data.Append(tabs);
            data.Append("{\r\n");
        }
        void WriteScopeContent(CustomNode node, int subIndex)
        {
            string tabs = getTabs(subIndex + 1);

            // Skriver ut attributerna + värdet
            for (int i = 0; i < node.Properties.Count; i++)
            {
                data.Append(tabs);
                data.Append(node.Properties[i].Name);
                data.Append(" = ");
                data.Append(node.Properties[i].Value);
                data.Append(";\r\n");
            }

            // Skriver ut inner-scopes
            for (int i = 0; i < node.Nodes.Count; i++)
            {
                WriteScopeStart(node.Nodes[i], subIndex + 1);
                WriteScopeContent(node.Nodes[i], subIndex + 1);
                WriteScopeEnd(subIndex + 1);
            }
        }
        void WriteScopeEnd(int subIndex)
        {
            string tabs = getTabs(subIndex);
            data.Append(tabs);
            data.Append("}\r\n");
        }

        /////////////////////////////////////////////////////////
        // READ
        /////////////////////////////////////////////////////////
        void ParseScope(string data, CustomNode inNode)
        {
            // Leta fram första ["Någonting"]
            int startSquareScope = data.IndexOf('[') + 1;
            int endSquareScope = data.IndexOf(']');
            string scopeName = data.Substring(startSquareScope, (endSquareScope - startSquareScope));
            inNode.Name = scopeName;

            // Sen letar vi efter första {
            // Fram till förs sin } (Ifall det är {,} mellan så ska den komma ihåg det så att den inte tar någon } från sin sub-nod)
            int startScope = data.IndexOf('{', endSquareScope) + 1;
            int endScope = -1;
            int i = startScope;
            int scopeCount = 1;
            while (true)
            {
                if (data[i] == '{') ++scopeCount;
                else if (data[i] == '}') --scopeCount;

                if (scopeCount == 0) // Klar
                {
                    endScope = i; // Hitta sin sista scope

                    ///////////////////////////////////////////////////
                    // Nu börjar vi parsa det som är innanför detta
                    ///////////////////////////////////////////////////
                    string subNode = data.Substring(startScope, endScope - startScope);

                    // Tar den ut Attributerna
                    int firstStop = subNode.IndexOf(';');
                    int firstSquareScopeStop = subNode.IndexOf('[');
                    if (firstStop != -1) // Det fanns attributer
                    {
                        string[] sliptedAttributes = null;

                        if (firstStop < firstSquareScopeStop)
                        {
                            sliptedAttributes = subNode.Substring(0, firstSquareScopeStop).Split(';');
                        }
                        else if (firstSquareScopeStop == -1)
                        {
                            sliptedAttributes = subNode.Split(';');
                        }

                        // Om det fanns attributer
                        if (sliptedAttributes != null)
                        {
                            // Loopar alla attributer
                            for (int j = 0; j < sliptedAttributes.Length; j++)
                            {
                                string propertyRow = sliptedAttributes[j].Trim();
                                int start = propertyRow.IndexOf('=');
                                if (start == -1)
                                {
                                    break;
                                }

                                string key = propertyRow.Substring(0, start).Trim();
                                string value = propertyRow.Substring(start + 1).Trim();
                                inNode.AddProperty(key, value);
                            }
                        }
                    }

                    // Hittar nya nodes
                    int nodeStart = 0;
                    int nodeScopeEnd = 0;
                    int nodeScopeCount = -1;
                    for (int k = 0; k < subNode.Length; k++)
                    {
                        if (subNode[k] == '[' && nodeScopeCount == -1)
                            nodeStart = k;
                        else if (subNode[k] == '{')
                        {
                            if (nodeScopeCount == -1)
                                nodeScopeCount = 1;
                            else
                                ++nodeScopeCount;
                        }
                        else if (subNode[k] == '}')
                        {
                            --nodeScopeCount;
                            if (nodeScopeCount == 0)
                            {
                                // Hittade sista scopet
                                nodeScopeEnd = k + 1;

                                string nodeScope = subNode.Substring(nodeStart, nodeScopeEnd - nodeStart);

                                // Skapar nya sub-noden
                                CustomNode newSubNode = new CustomNode();

                                // lägger in sub-noden i parent-noden
                                inNode.AddNode(newSubNode);

                                ParseScope(nodeScope, newSubNode);

                                nodeScopeCount = -1;
                            }
                        }
                    }

                    break;
                }
                ++i;
            }
        }

        /////////////////////////////////////////////////////////
        // Helpers  1§§
        /////////////////////////////////////////////////////////
        string getTabs(int subIndex)
        {
            string tabs = "";
            for (int i = 0; i < subIndex; i++) tabs += TAB;
            return tabs;
        }
        public byte[] StrToByteArray(string str)
        {
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
            return encoding.GetBytes(str);
        }
        public string ByteArrayToStr(byte[] bytes)
        {
            return System.Text.UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length); // Only Windows
        }

    }
}
